package wiki.xsx.jg.core;

import java.math.BigDecimal;
import java.sql.*;
import java.sql.Date;
import java.util.*;

/**
 * mysql实现类
 */
public class MysqlService implements DatabaseService {
    private String driverClassName;
    private String dbUrl;
    private String userName;
    private String password;

    public MysqlService(String driverClassName, String dbUrl, String userName, String password) {
        this.driverClassName = driverClassName;
        this.dbUrl = dbUrl;
        this.userName = userName;
        this.password = password;
    }

    @Override
    public Map<String, Map<String, Class<?>>> getTableInfo(String tableNames) throws Exception {
        return CommonService.getTableInfo(tableNames, this);
    }

    @Override
    public void createTables(Map<String, Map<String, List<Map<String, Class<?>>>>> classInfoMap) throws Exception {
        CommonService.createTables(classInfoMap,  this);
    }

    @Override
    public String getAllTableSQL() {
        StringBuilder builder = new StringBuilder();
        builder.append("SELECT TABLE_NAME AS name ")
                .append("FROM information_schema.TABLES ")
                .append("WHERE TABLE_TYPE='BASE TABLE' ")
                .append("AND TABLE_SCHEMA='").append(this.getDBName()).append("'");
        return builder.toString();
    }

    @Override
    public String getTableStructureSQL(String tableName) {
        StringBuilder builder = new StringBuilder();
        builder.append("SELECT * FROM ")
                .append("`").append(tableName).append("`")
                .append(" LIMIT 0, 1 ");
        return builder.toString();
    }

    @Override
    public String getDropTableSQL(String className) {
        StringBuilder sql = new StringBuilder();
        sql.append("DROP TABLE IF EXISTS `").append(this.getDBName()).append("`.").append(className);
        return sql.toString();
    }

    @Override
    public String getCreateTableSQL(
            String className,
            List<Map<String, Class<?>>> primaryList,
            List<Map<String, Class<?>>> isNotNullList,
            List<Map<String, Class<?>>> isNullList
    ) {
        StringBuilder pk = new StringBuilder();
        StringBuilder sql = new StringBuilder();
        sql.append("CREATE TABLE ").append(className).append("(");
        for (Map<String, Class<?>> primary : primaryList){
            for (Map.Entry<String, Class<?>> entry : primary.entrySet()){
                sql.append("`").append(entry.getKey()).append("` ")
                    .append(getDataType(entry.getValue())).append(" NOT NULL,");

                pk.append("`").append(entry.getKey()).append("`,");
            }
        }
        for (Map<String, Class<?>> isNotNull : isNotNullList){
            for (Map.Entry<String, Class<?>> entry : isNotNull.entrySet()){
                sql.append("`").append(entry.getKey()).append("` ")
                    .append(getDataType(entry.getValue())).append(" NOT NULL,");
            }
        }
        for (Map<String, Class<?>> isNull : isNullList){
            for (Map.Entry<String, Class<?>> entry : isNull.entrySet()){
                sql.append("`").append(entry.getKey()).append("` ")
                    .append(getDataType(entry.getValue())).append(",");
            }
        }
        if (pk.length()>0){
            sql.append("PRIMARY KEY (").append(pk.substring(0, pk.length()-1)).append(")");
        }else{
            sql = sql.deleteCharAt(sql.length()-1);
        }
        sql.append(")");
        return sql.toString();
    }

    @Override
    public Class<?> getTypeClass(String typeName) {
        if (typeName.matches("CHAR|VARCHAR")){
            return String.class;
        }else if(typeName.matches("TINYINT|SMALLINT|MEDIUMINT|INT|INTEGER|YEAR")) {
            return Integer.class;
        }else if(typeName.matches("BIGINT")) {
            return Long.class;
        }else if(typeName.matches("FLOAT")) {
            return Float.class;
        }else if(typeName.matches("DOUBLE")) {
            return Double.class;
        }else if(typeName.matches("DECIMAL")) {
            return BigDecimal.class;
        }else if(typeName.matches("DATE|DATETIME|TIMESTAMP")) {
            return Date.class;
        }else if(typeName.matches("TIME")) {
            return Time.class;
        }else if(typeName.matches("BINARY|VARBINARY|TINYBLOB|BLOB|MEDIUMBLOB|LONGBLOB")) {
            return byte[].class;
        }else{
            return Object.class;
        }
    }

    @Override
    public String getDataType(Class<?> type) {
        if (type.getSimpleName().equalsIgnoreCase("String")){
            return "VARCHAR(255)";
        }else if(type.getSimpleName().equalsIgnoreCase("Integer")){
            return "INT";
        }else if(type.getSimpleName().equalsIgnoreCase("Long")){
            return "BIGINT";
        }else if(type.getSimpleName().equalsIgnoreCase("Float")){
            return "FLOAT";
        }else if(type.getSimpleName().equalsIgnoreCase("Double")){
            return "DOUBLE";
        }else if(type.getSimpleName().equalsIgnoreCase("BigDecimal")){
            return "DECIMAL";
        }else if(type.getSimpleName().equalsIgnoreCase("Date")){
            return "TIMESTAMP";
        }else if(type.getSimpleName().equalsIgnoreCase("Time")){
            return "TIME";
        }else if(type.getSimpleName().equalsIgnoreCase("byte[]")){
            return "BLOB";
        }else{
            return "VARCHAR(255)";
        }
    }

    @Override
    public String getDBName() {
        if (this.dbUrl.contains("?")){
            return this.dbUrl.substring(this.dbUrl.lastIndexOf("/")+1, this.dbUrl.indexOf("?"));
        }else{
            return this.dbUrl.substring(this.dbUrl.lastIndexOf("/")+1);
        }
    }

    @Override
    public String getDriverClassName() {
        return this.driverClassName;
    }

    @Override
    public String getDbUrl() {
        return this.dbUrl;
    }

    @Override
    public String getDbUserName() {
        return this.userName;
    }

    @Override
    public String getDbPassword() {
        return this.password;
    }
}
